package com.qf.sl.youshare.utils;

import android.content.Context;
import android.os.Environment;

import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Created by IDEA
 * User : sl
 * on 2015/2/6.
 * 18:02
 */
public class FileCache {
    private static FileCache ourInstance;
    private final File fileCache;

    /**
     * 这个方法必须在 getInstance() 之前调用,主要用于初始化 Android 中特有的Context和应用程序
     *  私有缓存目录的功能
     * @param context
     * @return
     */
    public static FileCache createInstance(Context context){
        if (ourInstance == null) {
            ourInstance = new FileCache(context);
        }
        return ourInstance;
    }

    public static FileCache getInstance() {
        if (ourInstance == null){
            throw new IllegalStateException
                    ("FileCache must invoke createInstance(Context) before getInstance()");
        }
        return ourInstance;
    }

    //Android 上下文 Context
    private Context context;

    /**
     * 当前文件缓存的目录,主要针对 网址 上的资源,下载到本地的情况
     */
    private File imageCacheDir;

    /**
     * 在私有构造方法中指定参数, 那么这个构造方法必须由当前类
     *  进行初始化, 那么必须在静态方法中调用
     */
    private FileCache(Context context) {
        this.context = context;

        if (context != null) {
            //上下文有, 那么进行存储设置
            String state = Environment.getExternalStorageState();

            if (state.equals(Environment.MEDIA_MOUNTED)){
                //SD 卡存在
                fileCache = context.getExternalCacheDir();
            }else {
                fileCache = context.getCacheDir();
            }

        }else {
            //上下文没有,只能自己用SD卡存储,一个自定义目录
            String state = Environment.getExternalStorageState();
            if (state.equals(Environment.MEDIA_MOUNTED)){
                File directory = Environment.getExternalStorageDirectory();
                fileCache = new File(directory,".youshare");
            }else {
                throw new IllegalArgumentException("FileCache must set context or device has a SDcard. ");
            }
        }

        if (fileCache != null) {
            if (!fileCache.exists()){
                fileCache.mkdirs();
            }
        }else {
            throw new IllegalArgumentException("FileCache can`t get a cache dir.");
        }
    }

    /**
     * 获取url代表的数据,从本地存储中,如果有本地文件,
     *  那么返回有效,否则返回null
     * @param url
     * @return
     */
    public byte[] getFileContent(String url){
        byte[] ret = null;

        if (url != null) {

            //TODO URL 中通常包含了不能够作为文件名的一些字符,这种情况要进行映射
            //将 URL 转变为 唯一的, 可以作为文件名存在的字符串.
            //接触一个叫 MD5 的信息摘要算法

            String fileName = mapUrlToFile(url);
            if (fileName.length() > 0){
                File targetFile = new File(fileCache,fileName);
                if (targetFile.exists()){
                    //TODO 读取文件到字节数组

                    FileInputStream fin = null;

                    try {
                        fin = new FileInputStream(targetFile);
                        ret = StreamUtils.readStream(fin);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }finally {
                        if (fin != null) {
                            try {
                                fin.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                }
            }

        }

        return ret;
    }

    /**
     * 将下载的内容根据 url 映射规则, 保存到相应的文件中
     * @param url
     * @param data
     */
    public void putContent(String url, byte[] data){
        if (url != null && data != null) {
            String fileName = mapUrlToFile(url);

            File targetFile = new File(fileCache,fileName);

            //如果文件不存在,必须通过 createNewFile 进行调用创建后才可以写文件

            boolean bok = true;
            if (!targetFile.exists()){
                try {
                    bok = targetFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            //bok 表示文件存在
            if (bok){
                FileOutputStream fout = null;
                try {
                    fout = new FileOutputStream(targetFile);
                    fout.write(data);

                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        fout.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 将 url 转变为 文件名
     * @param url
     * @return
     */
    private static String mapUrlToFile(String url){
       String ret = null;

        if (url != null) {
            //TODO 将 url 进行 MD5 消息摘要
            // MD5 将任何一个内容转变为 不可逆的,唯一的 一个字节数组

            byte[] md5 = md5(url.getBytes());
            ret = hex(md5);

        }

        return ret;
    }

    /**
     * MD5算法将输入的字节数组 转换为 唯一的 一个字节组信息
     * @param data
     * @return
     */
    private static byte[] md5(byte[] data){
        byte[] ret = null;

        if (data != null) {
            try {
                MessageDigest digest = MessageDigest.getInstance("MD5");

                ret = digest.digest(data);//将 data 进行消息摘要, 生成一个特定的字节数组, 当中的数值不可逆转

            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }

        return ret;
    }

    /**
     * 将字节数组中的每一个字节转换为 0-9 a-f 这些可以显示的十六进制表示法的字符
     * @param data
     * @return
     */
    private static String hex(byte[] data){
        StringBuilder sb = new StringBuilder();

        if (data != null && data.length > 0) {
            for (byte b : data) {
                int h, l;

                h = (b>>4)&0xff;
                l = b & 0xff;

                sb.append(Integer.toHexString(h)).append(Integer.toHexString(l));
            }
        }

        return sb.toString();
    }
}
